package com.gk.javaservice.util.base;
/**
 结构如下(每部分用-分开):
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* 1 位标识，由于 long 基本类型在 Java 中是带符号的，最高位是符号位，正数是 0，负数是 1，所以 id 一般是正数，最高位是 0
* 41 位时间戳(毫秒级)，注意，41 位时间戳不是存储当前时间的时间戳，而是存储时间戳的差值（当前时间戳 - 开始时间戳)
* 得到的值），这里的的开始时间戳，一般是我们的 id 生成器开始使用的时间，由我们程序来指定的（如下面程序 SnowflakeIdWorker 类的 startTime 属性）。41 位的时间戳，可以使用 69 年，年 T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* 10 位的数据机器位，可以部署在 1024 个节点，包括 5 位 datacenterId 和 5 位 workerId
* 12 位序列，毫秒内的计数，12 位的计数顺序号支持每个节点每毫秒(同一机器，同一时间戳)产生 4096 个 ID 序号
* 加起来刚好 64 位，为一个 Long 型。
*/
public class SnowflakeIdWorker {

   /* 开始时间戳 (2018-03-04) */
   private final long twepoch = 1520148401625L;
   /**
    * 机器id所占的位数
    */
   private final long workerIdBits = 5L;

   /**
    * 数据标识id所占的位数
    */
   private final long datacenterIdBits = 5L;

   /**
    * 支持的最大机器id，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
    */
   private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

   /**
    * 支持的最大数据标识id，结果是31
    */
   private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

   /**
    * 序列在id中占的位数
    */
   private final long sequenceBits = 12L;

   /**
    * 机器ID向左移12位
    */
   private final long workerIdShift = sequenceBits;

   /**
    * 数据标识id向左移17位(12+5)
    */
   private final long datacenterIdShift = sequenceBits + workerIdBits;

   /**
    * 时间戳向左移22位(5+5+12)
    */
   private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

   /**
    * 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095)
    */
   private final long sequenceMask = -1L ^ (-1L << sequenceBits);

   /**
    * 工作机器ID(0~31)
    */
   private long workerId;

   /**
    * 数据中心ID(0~31)
    */
   private long datacenterId;

   /**
    * 毫秒内序列(0~4095)
    */
   private long sequence = 0L;

   /**
    * 上次生成ID的时间戳
    */
   private long lastTimestamp = -1L;

//==============================Constructors=====================================

   /**
    * 构造函数
    *
    * @param workerId     工作ID (0~31)
    * @param datacenterId 数据中心ID (0~31)
    */
   public SnowflakeIdWorker(long workerId, long datacenterId) {
       if (workerId > maxWorkerId || workerId < 0) {
           throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
       }
       if (datacenterId > maxDatacenterId || datacenterId < 0) {
           throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
       }
       this.workerId = workerId;
       this.datacenterId = datacenterId;
   }

//==============================Methods==========================================

   /**
    * 获得下一个ID (该方法是线程安全的)
    *
    * @return SnowflakeId
    */
   public  synchronized  long nextId() {
       long timestamp = timeGen();

       //如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
       if (timestamp < lastTimestamp) {
           throw new RuntimeException(
                   String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
       }

       //如果是同一时间生成的，则进行毫秒内序列
       if (lastTimestamp == timestamp) {
           sequence = (sequence + 1) & sequenceMask;
           //毫秒内序列溢出
           if (sequence == 0) {
               //阻塞到下一个毫秒,获得新的时间戳
               timestamp = tilNextMillis(lastTimestamp);
           }
       }
       //时间戳改变，毫秒内序列重置
       else {
           sequence = 0L;
       }

       //上次生成ID的时间戳
       lastTimestamp = timestamp;

       //移位并通过或运算拼到一起组成64位的ID
       return ((timestamp - twepoch) << timestampLeftShift) //
               | (datacenterId << datacenterIdShift) //
               | (workerId << workerIdShift) //
               | sequence;
   }

   /**
    * 阻塞到下一个毫秒，直到获得新的时间戳
    *
    * @param lastTimestamp 上次生成ID的时间戳
    * @return 当前时间戳
    */
   protected long tilNextMillis(long lastTimestamp) {
       long timestamp = timeGen();
       while (timestamp <= lastTimestamp) {
           timestamp = timeGen();
       }
       return timestamp;
   }

   /**
    * 返回以毫秒为单位的当前时间
    *
    * @return 当前时间(毫秒)
    */
   protected long timeGen() {
       return System.currentTimeMillis();
   }

   /**
    * 测试
    */
//   public static void main(String[] args) {
//       // System.out.println("开始：" + System.currentTimeMillis());
//       SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
//       long id = idWorker.nextId();
//       long id2 = idWorker.nextId();
//       long id3 = idWorker.nextId();
//       long id4 = idWorker.nextId();
//       long id5 = idWorker.nextId();
//       long id21 = idWorker.nextId();
//       long id31 = idWorker.nextId();
//       long id41 = idWorker.nextId();
//       // System.out.println(id);
//       // System.out.println(id2);
//       // System.out.println(id3);
//       // System.out.println(id4);
//       // System.out.println(id5);
//       // System.out.println(id21);
//       // System.out.println(id31);
//       // System.out.println(id41);
//   }
}
